package cn.edu.zut.qixiang.mock;

import lombok.Getter;

import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;

/**
 * 辐射数据采集表数据生成器
 * 为辐射数据采集站生成模拟数据（每隔1小时采集一次）
 * @author 86178
 */
public class UVDataGenerator {

    // 数据库配置
    private static final String URL = "jdbc:mysql://localhost:3306/qixiang_base?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8";
    private static final String USER = "root";
    private static final String PASSWORD = "520521fcf..@1234";

    // 数据生成参数
    private static final int BATCH_SIZE = 100;
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        generateRadiationData();
    }

    /**
     * 生成辐射数据（每隔1小时采集一次）
     */
    public static void generateRadiationData() {
        String selectStationsSql = "SELECT station_id FROM collection_stations WHERE station_type = '辐射数据采集站' ORDER BY station_id";
        String insertDataSql = "INSERT INTO uv_data (collection_station_id, uva, uvi, uvb, sunshine_duration, collection_time) VALUES (?, ?, ?, ?, ?, ?)";

        try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
             PreparedStatement selectStmt = conn.prepareStatement(selectStationsSql);
             PreparedStatement insertStmt = conn.prepareStatement(insertDataSql)) {

            conn.setAutoCommit(false);
            Random random = new Random();

            // 获取所有辐射数据采集站
            ResultSet stationRs = selectStmt.executeQuery();

            int batchCount = 0;
            int totalRecords = 0;
            int stationCount = 0;

            // 生成2025年5月1日到9月1日的数据，每1小时一次
            LocalDateTime startTime = LocalDateTime.of(2025, 5, 1, 0, 0, 0);
            LocalDateTime endTime = LocalDateTime.of(2025, 9, 1, 23, 0, 0);

            System.out.println("开始生成辐射数据...");
            System.out.println("时间范围: " + startTime.format(DATE_FORMATTER) + " 到 " + endTime.format(DATE_FORMATTER));
            System.out.println("采集频率: 每1小时一次");

            // 遍历每个辐射采集站
            while (stationRs.next()) {
                int stationId = stationRs.getInt("station_id");
                stationCount++;
                System.out.println("为采集站 " + stationId + " 生成数据...");

                LocalDateTime currentTime = startTime;

                // 为每个时间点生成数据（每1小时一次）
                while (!currentTime.isAfter(endTime)) {
                    // 生成辐射数据
                    RadiationData radiationData = generateRadiationDataForTime(currentTime, random, stationId);

                    // 设置插入参数
                    insertStmt.setInt(1, stationId);
                    insertStmt.setDouble(2, radiationData.getUva());
                    insertStmt.setDouble(3, radiationData.getUvi());
                    insertStmt.setDouble(4, radiationData.getUvb());
                    insertStmt.setDouble(5, radiationData.getSunshineDuration());
                    insertStmt.setString(6, currentTime.format(DATE_FORMATTER));

                    insertStmt.addBatch();
                    batchCount++;
                    totalRecords++;

                    // 批量提交
                    if (batchCount % BATCH_SIZE == 0) {
                        insertStmt.executeBatch();
                        conn.commit();
                        System.out.println("已提交 " + batchCount + " 条记录，总计 " + totalRecords + " 条");
                    }

                    // 时间增加1小时
                    currentTime = currentTime.plusHours(1);
                }
            }

            // 提交剩余数据
            if (batchCount % BATCH_SIZE != 0) {
                insertStmt.executeBatch();
                conn.commit();
            }

            System.out.println("辐射数据生成完成！");
            System.out.println("共为 " + stationCount + " 个采集站生成 " + totalRecords + " 条记录");

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 为指定时间生成辐射数据
     */
    private static RadiationData generateRadiationDataForTime(LocalDateTime time, Random random, int stationId) {
        RadiationData data = new RadiationData();
        int hour = time.getHour();
        int month = time.getMonthValue();

        // 根据月份调整基准辐射强度（夏季辐射最强）
        double seasonalFactor = getSeasonalFactor(month);

        // 根据小时计算太阳高度角影响（正午辐射最强）
        double solarAngleFactor = calculateSolarAngleFactor(hour);

        // 天气影响（云量影响）
        double weatherFactor = calculateWeatherFactor(time, random);

        // 生成UVA辐射强度 (W/m²)
        double uvaBase = 45.0 * seasonalFactor;
        data.setUva(Math.round((uvaBase * solarAngleFactor * weatherFactor + (random.nextDouble() - 0.5) * 5) * 100) / 100.0);

        // 生成UVB辐射强度 (W/m²) - UVB通常比UVA弱
        double uvbBase = 3.5 * seasonalFactor;
        data.setUvb(Math.round((uvbBase * solarAngleFactor * weatherFactor + (random.nextDouble() - 0.5) * 0.5) * 100) / 100.0);

        // 生成紫外线指数 (UVI)
        data.setUvi(generateUVI(data.getUva(), data.getUvb(), hour, month));

        // 生成日照时长（小时） - 只有白天有日照
        data.setSunshineDuration(generateSunshineDuration(hour, weatherFactor, random));

        return data;
    }

    /**
     * 获取季节因子（夏季辐射最强）
     */
    private static double getSeasonalFactor(int month) {
        switch (month) {
            case 5: // 5月：春末
                return 0.8;
            case 6: // 6月：初夏
                return 0.95;
            case 7: // 7月：盛夏
                return 1.0;
            case 8: // 8月：盛夏
                return 0.98;
            case 9: // 9月：初秋
                return 0.75;
            default:
                return 0.7;
        }
    }

    /**
     * 计算太阳高度角影响因子（正午最强，早晚最弱）
     */
    private static double calculateSolarAngleFactor(int hour) {
        if (hour < 6 || hour > 18) {
            return 0.0; // 夜间无太阳辐射
        }

        // 使用正弦函数模拟太阳高度角变化
        // 正午12时太阳高度角最大，影响因子为1
        double normalizedHour = (hour - 6) / 12.0 * Math.PI;
        return Math.sin(normalizedHour);
    }

    /**
     * 计算天气影响因子（云量对辐射的削弱）
     */
    private static double calculateWeatherFactor(LocalDateTime time, Random random) {
        // 使用日期作为种子，使同一日期的天气一致
        long daySeed = time.toLocalDate().toEpochDay();
        Random dayRandom = new Random(daySeed);

        // 模拟天气类型：0-晴天, 1-少云, 2-多云, 3-阴天
        int weatherType = dayRandom.nextInt(4);

        switch (weatherType) {
            case 0: // 晴天：辐射最强
                return 0.95 + dayRandom.nextDouble() * 0.05;
            case 1: // 少云：辐射较强
                return 0.8 + dayRandom.nextDouble() * 0.15;
            case 2: // 多云：辐射中等
                return 0.5 + dayRandom.nextDouble() * 0.2;
            case 3: // 阴天：辐射最弱
                return 0.1 + dayRandom.nextDouble() * 0.2;
            default:
                return 0.7;
        }
    }

    /**
     * 生成紫外线指数 (UVI)
     */
    private static double generateUVI(double uva, double uvb, int hour, int month) {
        if (hour < 6 || hour > 18) {
            return 0.0; // 夜间无紫外线
        }

        // UVI基于UVA和UVB辐射强度计算
        double uviBase = (uva * 0.1 + uvb * 2.0) / 10.0;

        // 根据季节调整
        double seasonalBoost = 0.0;
        switch (month) {
            case 6:
            case 7:
            case 8:
                seasonalBoost = 2.0; // 夏季UVI更高
                break;
        }

        double uvi = uviBase + seasonalBoost;
        return Math.max(0.0, Math.round(uvi * 10) / 10.0);
    }

    /**
     * 生成日照时长（小时）
     */
    private static double generateSunshineDuration(int hour, double weatherFactor, Random random) {
        if (hour < 6 || hour > 18) {
            return 0.0; // 夜间无日照
        }

        // 日照时长受天气影响
        // 晴天：接近1小时，阴天：接近0小时
        double sunshine = weatherFactor * (0.8 + random.nextDouble() * 0.2);

        return Math.round(sunshine * 10) / 10.0;
    }

    /**
     * 辐射数据实体类
     */
    @Getter
    static class RadiationData {
        private double uva;
        private double uvi;
        private double uvb;
        private double sunshineDuration;

        public void setUva(double uva) { this.uva = uva; }

        public void setUvi(double uvi) { this.uvi = uvi; }

        public void setUvb(double uvb) { this.uvb = uvb; }

        public void setSunshineDuration(double sunshineDuration) { this.sunshineDuration = sunshineDuration; }
    }
}